{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Numpy实现逻辑回归 - 识别猫\n",
    "\n",
    "在该实验中我们将介绍如何使用Python及Numpy lib库实现Logistic回归模型来识别猫。在实现过程中，读者将会学习到神经网络基本结构的配置，其中的关键知识点包括初始化参数、计算成本、计算梯度、优化参数。需要注意的是，在具体的编码实现中会大量是用到Numpy lib库的基本操作，不熟悉numpy操作的读者可以回顾numpy操作内容，方便后续的学习。下面就进入编程实战部分。\n",
    "\n",
    "** 图片处理 **\n",
    "\n",
    "由于识别猫问题涉及到图片处理指示，这里对计算机如何保存图片做一个简单的介绍。在计算机中，图片被存储为三个独立的矩阵，分别对应图3-6中的红、绿、蓝三个颜色通道，如果图片是64*64像素的，就会有三个64*64大小的矩阵，要把这些像素值放进一个特征向量中，需要定义一个特征向量X，将三个颜色通道中的所有像素值都列出来。如果图片是64*64大小的，那么特征向量X的总纬度就是64*64*3，也就是12288维。这样一个12288维矩阵就是Logistic回归模型的一个训练数据。\n",
    "\n",
    "<img src=\"images/image_to_vector.png\" style=\"width:550px;height:300px;\">\n",
    "\n",
    "## 1 - 引用库\n",
    "\n",
    "首先，载入几个需要用到的库，它们分别是：\n",
    "- numpy：一个python的基本库，用于科学计算\n",
    "- matplotlib.pyplot：用于生成图，在验证模型准确率和展示成本变化趋势时会使用到\n",
    "- lr_utils：定义了load_dataset()方法用于载入数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "from lr_utils import load_dataset"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2 - \t载入数据\n",
    "\n",
    "猫的图片数据集以hdf5文件的形式存储，包含了如下内容：\n",
    "\n",
    "- 训练数据集：包含了m_train个图片的数据集，数据的标签（Label）分为cat（y=1）和non-cat（y=0）两类。\n",
    "\n",
    "- 测试数据集：包含了m_test个图片的数据集，数据的标签（Label）同（1）。\n",
    "\n",
    "单个图片数据的存储形式为（num_x, num_x, 3），其中num_x表示图片的长或宽（数据集图片的长和宽相同），数字3表示图片的三通道（RGB）。\n",
    "\n",
    "在代码中使用一行代码来读取数据，我们暂不需要了解数据的读取过程，只需调用load_dataset()方法，并存储五个返回值，以便后续的使用。\n",
    "\n",
    "需要注意的是，添加“_orig”后缀表示该数据为原始数据，因为之后还需要对数据进行进一步处理。未添加“_orig”的数据则表示之后不对该数据作进一步处理。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# 读取数据(cat/non-cat)\n",
    "train_set_x_orig, train_set_y, test_set_x_orig, test_set_y, classes = load_dataset()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上述数据共包含五个部分，分别是训练和测试数据集train_set_x_orig、test_set_x_orig以及对应的标签集train_set_y，test_set_y，还有一个分类列表classes。以训练数据集train_set_x_orig为例，其中每一行都是一个表示图像的三维数组。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3 - 数据预处理\n",
    "\n",
    "获取数据后的下一步工作是获得数据的相关信息，如训练数据个数m_train、测试数据个数m_test和图片的长度或宽度num_x，下述代码使用numpy.array.shape来获取数据的相关信息。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# 获取数据相关信息\n",
    "m_train = train_set_x_orig.shape[0]\n",
    "m_test = test_set_x_orig.shape[0]\n",
    "# 本例中num_px=64\n",
    "num_px = train_set_x_orig.shape[1]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "接下来需要对数据作进一步处理，为了便于训练，可以忽略图片的结构信息，将包含图像长、宽和通道数信息的三维数组压缩成一维数组，图片数据的形状将由(64, 64, 3)转化为(64 * 64 * 3, 1)，代码清单3-7给出了转换数据形状的方式。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# 转换数据形状\n",
    "train_set_x_flatten = train_set_x_orig.reshape(m_train,-1).T\n",
    "test_set_x_flatten = test_set_x_orig.reshape(m_test,-1).T"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在开始训练之前，还需要对数据进行归一化处理。图片采用红、绿、蓝三通道的方式来表示颜色，每个通道的单个像素点都存储着一个0-255的像素值，所以图片的归一化处理十分简单，只需要将数据集中的每个值除以255即可，但需要注意的是结果值应为float类型，直接除以255会导致结果错误，在Python中除以255.即可将结果转化为float类型，下述代码给出了数据归一化过程。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "train_set_x = train_set_x_flatten/255.\n",
    "test_set_x = test_set_x_flatten/255."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4 - 模型训练\n",
    "\n",
    "完成了数据处理工作，下面开始进入模型训练过程。其中有四个关键步骤分别为：\n",
    "- 初始化模型参数（Parameters）\n",
    "- 循环：\n",
    "    - 计算成本（Cost）\n",
    "    - 计算梯度（Gradient）\n",
    "    - 更新参数（Gradient Descent）\n",
    "- 利用模型进行预测\n",
    "- 分析预测结果\n",
    "\n",
    "首先，实现Sigmoid()激活函数如下代码所示，较为简单，不再赘述："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def sigmoid(z):\n",
    "    s = 1 / (1 + np.exp(-z))\n",
    "    return s"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "接下来开始初始化模型参数，定义函数initialize_with_zeros()如代码所示，首先使用numpy.zeros()将w初始化为(dim, 1)形状的零向量，其中dim表示w参数的个数，它的值等于训练数据的特征数，即每张图片的像素点个数。然后再将b初始化为0即可。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def initialize_with_zeros(dim):\n",
    "    # 将w初始化为(dim, 1)形状的零向量，其中dim表示w参数个数\n",
    "    # 将b初始化为零\n",
    "    w = np.zeros((dim, 1), dtype = np.float)\n",
    "    b = 0\n",
    "    \n",
    "    return w, b"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "初始化模型参数后，接下来定义前向播和后向传播过程，这两个过程包含在 propagate()函数中。\n",
    "\n",
    "函数propagate()的关键内容是计算成本函数（Cost）和梯度（Gradient），具体的实现如下述代码所示，其中m=X.shape[1]表示数据个数，A表示预测结果，cost表示成本函数，dw和db分别表示对应的梯度。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def propagate(w, b, X, Y):\n",
    "    \"\"\"\n",
    "    计算成本cost和梯度grads\n",
    "        Args:\n",
    "            w -- 权重， (num_px * num_px * 3, 1)维的numpy数组\n",
    "            b -- 偏置bias，标量\n",
    "            X -- 数据，形状为(num_px * num_px * 3, number of examples)\n",
    "            Y -- 数据的真实标签(包含值 0 if non-cat, 1 if cat) ，形状为 (1, number of examples)\n",
    "        Return:\n",
    "            cost -- 逻辑回归的损失函数\n",
    "            dw -- cost对参数w的梯度，形状与参数w一致\n",
    "            db -- cost对参数b的梯度，形状与参数b一致\n",
    "    \"\"\"\n",
    "    \n",
    "    # m为数据个数\n",
    "    m = X.shape[1]\n",
    "    \n",
    "    # 前向传播，计算成本函数\n",
    "    A = sigmoid(np.dot(w.T,  X) + b)                                    \n",
    "    cost = np.sum(-(Y * np.log(A) + (1 - Y) * np.log(1 - A))) / m                                 \n",
    "    \n",
    "    # 后向传播，计算梯度\n",
    "    dw = np.dot(X, (A - Y).T) / m\n",
    "    db = np.sum((A - Y)) / m\n",
    "    cost = np.squeeze(cost)\n",
    "    \n",
    "    grads = {\"dw\": dw,\n",
    "             \"db\": db}\n",
    "    \n",
    "    return grads, cost\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "定义了成本函数和梯度的计算过程后，接下来定义优化函数optimize()使用梯度下降更新参数。具体实现如代码所示，关键内容为调用propagate()函数获取梯度值dw、db和cost，并根据梯度值来更新参数w和b。以w为例，具体更新过程为w -= learning_rate * dw。同时，在参数更新过程中，维护一个成本数组costs，每一百次迭代则记录一次成本，便于之后绘图分析成本变化趋势。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# 使用梯度下降更新参数w，b\n",
    "def optimize(w, b, X, Y, num_iterations, learning_rate, print_cost = False):\n",
    "    \"\"\"\n",
    "    使用梯度下降算法优化参数w和b\n",
    "        Args:\n",
    "            w -- 权重， (num_px * num_px * 3, 1)维的numpy数组\n",
    "            b -- 偏置bias，标量\n",
    "            X -- 数据，形状为(num_px * num_px * 3, number of examples)\n",
    "            Y -- 数据的真实标签(包含值 0 if non-cat, 1 if cat) ，形状为 (1, number of examples)\n",
    "            num_iterations -- 优化的迭代次数\n",
    "            learning_rate -- 梯度下降的学习率，可控制收敛速度和效果\n",
    "            print_cost -- 每一百次迭代输出一次cost\n",
    "            \n",
    "        Returns:\n",
    "            params -- 包含参数w和b的python字典\n",
    "            grads -- 包含梯度dw和db的python字典\n",
    "            costs -- 保存了优化过程cost的list，可以用于输出cost变化曲线\n",
    "        \"\"\"\n",
    "\n",
    "    costs = []\n",
    "    \n",
    "    for i in range(num_iterations):\n",
    "        # 调用propagate()获取梯度值grads和成本cost\n",
    "        grads, cost = propagate(w, b, X, Y)\n",
    "        dw = grads[\"dw\"]\n",
    "        db = grads[\"db\"]\n",
    "        \n",
    "        # 更新参数\n",
    "        w -= learning_rate * dw \n",
    "        b -= learning_rate * db\n",
    "        \n",
    "        # 记录costs\n",
    "        if i % 100 == 0:\n",
    "            costs.append(cost)\n",
    "        \n",
    "        # 每一百次迭代，打印一次cost\n",
    "        if print_cost and i % 100 == 0:\n",
    "            print (\"Cost after iteration %i: %f\" %(i, cost))\n",
    "    \n",
    "    params = {\"w\": w,\n",
    "              \"b\": b}\n",
    "    \n",
    "    grads = {\"dw\": dw,\n",
    "             \"db\": db}\n",
    "    \n",
    "    return params, grads, costs\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5 - 模型检验\n",
    "\n",
    "以上内容完成了模型的训练过程，得到了最终的参数w和b，接下来实现predict()函数使用训练完成的模型进行预测，具体实现如代码所示，输入参数w和b以及测试数据集X，预测结果A，并将连续值A转化为二分类结果0或1，存储在Y_prediction中。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# 使用模型进行预测\n",
    "def predict(w, b, X):    \n",
    "    \"\"\"\n",
    "    用学习到的逻辑回归模型来预测图片是否为猫（1 cat or 0 non-cat\n",
    "        Args:\n",
    "            w -- 权重， (num_px * num_px * 3, 1)维的numpy数组\n",
    "            b -- 偏置bias，标量\n",
    "            X -- 数据，形状为(num_px * num_px * 3, number of examples)\n",
    "            \n",
    "        Returns:\n",
    "            Y_prediction -- 包含了对X数据集的所有预测结果，是一个numpy数组或向量\n",
    "    \"\"\"\n",
    "\n",
    "    # m为数据个数\n",
    "    m = X.shape[1]\n",
    "    # 初始化Y_prediction为m维零向量\n",
    "    Y_prediction = np.zeros((1,m))\n",
    "    w = w.reshape(X.shape[0], 1)\n",
    "    # 预测结果A\n",
    "    A = sigmoid(np.dot(w.T, X) + b)\n",
    "\n",
    "    for i in range(A.shape[1]):\n",
    "        # 将连续值A转化为二分类结果0或1\n",
    "        if A[0, i] > 0.5:\n",
    "            Y_prediction[0, i] = 1\n",
    "        else:\n",
    "            Y_prediction[0, i] = 0\n",
    "    \n",
    "    return Y_prediction\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "至此，上述内容完成了Logistic回归模型的训练和预测过程，实现了几个关键函数：\n",
    "- sigmoid()：激活函数\n",
    "- initialize_with_zeros()：初始化参数w和b\n",
    "- propagate()：计算成本cost和梯度值dw、db\n",
    "- optimize()：利用梯度下降更新参数值\n",
    "- predict()：使用模型预测结果\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 6 - 实现model\n",
    "\n",
    "现在进行最后一步，实现一个model()函数，将所有函数合并，实现过程十分简单，只需将上述函数按顺序调用即可，具体实现如代码所示，需要注意的是代码中计算了训练准确度train_accuracy和测试准确度test_accuracy，方便读者评估Logistic回归模型。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# 合并所有函数\n",
    "def model(X_train, Y_train, X_test, Y_test, num_iterations = 2000, learning_rate = 0.5, print_cost = False):\n",
    "    \"\"\"\n",
    "    按顺序调用上述方法，构建整体逻辑回归模型model\n",
    "        Args:\n",
    "            X_train -- 训练数据，形状为(num_px * num_px * 3, m_train)\n",
    "            Y_train -- 训练数据的真实标签(包含值 0 if non-cat, 1 if cat) ，形状为 (1, m_train)\n",
    "            X_test -- 测试数据，形状为(num_px * num_px * 3, m_test)\n",
    "            Y_test -- 测试数据的真实标签(包含值 0 if non-cat, 1 if cat) ，形状为 (1, m_t      est)\n",
    "            w -- 权重， (num_px * num_px * 3, 1)维的numpy数组\n",
    "            b -- 偏置bias，标量\n",
    "            X -- 数据，形状为(num_px * num_px * 3, number of examples)\n",
    "            Y -- 数据的真实标签(包含值 0 if non-cat, 1 if cat) ，形状为 (1, number of examples)\n",
    "            num_iterations -- 优化的迭代次数\n",
    "            learning_rate -- 梯度下降的学习率，可控制收敛速度和效果\n",
    "            print_cost -- 每一百次迭代输出一次cost\n",
    "            \n",
    "        Returns:\n",
    "            d -- 包含模型信息的python字典\n",
    "    \"\"\"\n",
    "    \n",
    "    # 初始化参数w，b\n",
    "    w, b = initialize_with_zeros(X_train.shape[0])\n",
    "\n",
    "    # 梯度下降更新参数\n",
    "    parameters, grads, costs = optimize(w, b, X_train, Y_train, num_iterations, learning_rate, print_cost)\n",
    "    \n",
    "    # 获得参数w，b\n",
    "    w = parameters[\"w\"]\n",
    "    b = parameters[\"b\"]\n",
    "   \n",
    "    # 分别使用训练数据集和测试数据集进行预测\n",
    "    Y_prediction_test = predict(w, b, X_test)\n",
    "    Y_prediction_train = predict(w, b, X_train)\n",
    "\n",
    "    # 输出训练准确度和测试准确度\n",
    "    print(\"train accuracy: {} %\".format(100 - np.mean(np.abs(Y_prediction_train - Y_train)) * 100))\n",
    "    print(\"test accuracy: {} %\".format(100 - np.mean(np.abs(Y_prediction_test - Y_test)) * 100))\n",
    "    \n",
    "    d = {\"costs\": costs,\n",
    "         \"Y_prediction_test\": Y_prediction_test, \n",
    "         \"Y_prediction_train\" : Y_prediction_train, \n",
    "         \"w\" : w, \n",
    "         \"b\" : b,\n",
    "         \"learning_rate\" : learning_rate,\n",
    "         \"num_iterations\": num_iterations}\n",
    "    \n",
    "    return d\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "利用之前获取并处理过的数据，调用model()函数并开始训练。输出成本cost的变化并输出训练准确率train_accuray和测试准确率test_accuracy。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Cost after iteration 0: 0.693147\n",
      "Cost after iteration 100: 0.584508\n",
      "Cost after iteration 200: 0.466949\n",
      "Cost after iteration 300: 0.376007\n",
      "Cost after iteration 400: 0.331463\n",
      "Cost after iteration 500: 0.303273\n",
      "Cost after iteration 600: 0.279880\n",
      "Cost after iteration 700: 0.260042\n",
      "Cost after iteration 800: 0.242941\n",
      "Cost after iteration 900: 0.228004\n",
      "Cost after iteration 1000: 0.214820\n",
      "Cost after iteration 1100: 0.203078\n",
      "Cost after iteration 1200: 0.192544\n",
      "Cost after iteration 1300: 0.183033\n",
      "Cost after iteration 1400: 0.174399\n",
      "Cost after iteration 1500: 0.166521\n",
      "Cost after iteration 1600: 0.159305\n",
      "Cost after iteration 1700: 0.152667\n",
      "Cost after iteration 1800: 0.146542\n",
      "Cost after iteration 1900: 0.140872\n",
      "train accuracy: 99.043062201 %\n",
      "test accuracy: 70.0 %\n"
     ]
    }
   ],
   "source": [
    "d = model(train_set_x, train_set_y, test_set_x, test_set_y, num_iterations = 2000, learning_rate = 0.005, print_cost = True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "训练结果显示训练准确率达到99%，说明训练的模型可以准确的拟合训练数据，而测试准确率为70%，由于训练数据集较小并且Logistic回归是一个线性回归分类器，所以70%的准确率已经是一个不错的结果。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 7 - 预测\n",
    "\n",
    "获得预测结果后，读者可以查看模型对某张图片的预测是否准确，输出图片及其预测的分类结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "y = 1, you predicted that it is a \"cat\" picture.\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7f851d9f7850>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 分类错误的示例\n",
    "index = 12\n",
    "plt.imshow(test_set_x[:,index].reshape((num_px, num_px, 3)))\n",
    "print (\"y = \" + str(test_set_y[0,index]) + \", you predicted that it is a \\\"\" + classes[int(d[\"Y_prediction_test\"][0,index])].decode(\"utf-8\") +  \"\\\" picture.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以看到模型对这张图片的分类正确，将猫图片分类为cat。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 8 - 学习曲线\n",
    "\n",
    "现在，根据之前保存的costs输出成本的变化情况"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7f851da26810>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 输出学习曲线\n",
    "costs = np.squeeze(d['costs'])\n",
    "plt.plot(costs)\n",
    "plt.ylabel('cost')\n",
    "plt.xlabel('iterations (per hundreds)')\n",
    "plt.title(\"Learning rate =\" + str(d[\"learning_rate\"]))\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以看到，图中的成本随着迭代次数的增加而减小，这说明了参数w和b不断被学习和优化。\n",
    "\n",
    "至此，Logistic回归模型的Python代码实现已经介绍完毕，相信读者对Logistic回归有了更深刻的理解和把握。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "python2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
